package com.da.tools.json;

import com.da.exception.JsonException;

/**
 * @author da
 * @time 2025/3/5 上午 11:06
 * 使用有限状态机加递归下降去解析json
 */
public class JsonParser {
    private int index; // 当前解析到的位置
    private String json; // 要解析的json字符串

    /**
     * 解析json
     *
     * @param json json字符串
     * @return 解析后的对象(JsonObject或JsonArray)
     */
    public Object parse(String json) {
        if (json.isEmpty()) {
            throw new JsonException("请输入合法的json字符串");
        }
        // 去掉json前后的空格
        this.json = json.trim();
        // 从第一个字符开始解析
        this.index = 0;
        // json字符串的第一个字符必须是{或[
        if (json.charAt(0) == '{') {
            return parseObject();
        }
        if (json.charAt(0) == '[') {
            return parseArray();
        }
        throw new JsonException("请输入合法的json字符串");
    }

    /**
     * 语法验证方法
     *
     * @param expected 期望的字符
     * @throws RuntimeException 当实际字符与预期不符时抛出
     */
    private void match(char expected) {
        //验证对应位置的字符是不是指定的字符
        if (index >= json.length() || json.charAt(index) != expected) {
            throw new JsonException("JSON语法错误：位置 " + index + " 应为 '" + expected + "'");
        }
        //验证过后就让下标加1
        index++;
    }

    /**
     * 跳过所有空白字符
     * 包括：空格、制表符、换行符等
     */
    private void skipWhitespace() {
        while (index < json.length() && Character.isWhitespace(json.charAt(index))) {
            index++;
        }
    }

    /**
     * 处理转义字符
     *
     * @param c 转义字符
     * @return 处理后的字符
     */
    private char handleEscapeChar(char c) {
        switch (c) {
            case '"':
                return '"';
            case '\\':
                return '\\';
            case '/':
                return '/';
            case 'b':
                return '\b';
            case 'f':
                return '\f';
            case 'n':
                return '\n';
            case 'r':
                return '\r';
            case 't':
                return '\t';
            case 'u':
                // 处理 Unicode 转义序列
                if (index + 4 > json.length()) {
                    throw new JsonException("非法的Unicode转义序列");
                }
                String hex = json.substring(index, index + 4);
                index += 4;
                return (char) Integer.parseInt(hex, 16);
            default:
                throw new JsonException("无效的转义字符: " + c);
        }
    }

    /**
     * 解析对象
     *
     * @return 解析后的对象(JsonObject)
     */
    private JsonObject parseObject() {
        JsonObject obj = new JsonObject();
        // json字符串的第一个字符必须是{
        match('{');
        //下标位置小于json字符串的长度时进入循环
        while (index < json.length()) {
            //跳过所有空白字符
            skipWhitespace();
            //遇到 } 说明当前对象解析结束了
            if (json.charAt(index) == '}') {
                index++;
                break;
            }
            //解析键
            String key = parseString();
            //跳过所有空白字符
            skipWhitespace();
            //判断是不是跟着:
            match(':');
            //跳过所有空白字符
            skipWhitespace();
            //解析值
            Object value = parseValue();
            //将键值对放入对象中
            obj.put(key, value);
            //跳过所有空白字符
            skipWhitespace();
            //处理后面的分隔符(,)
            if (index < json.length() && json.charAt(index) == ',') {
                index++;
            }
        }
        //返回解析完成后的对象
        return obj;
    }

    /**
     * 解析数组
     *
     * @return 解析后的数组(JsonArray)
     */
    private JsonArray parseArray() {
        JsonArray array = new JsonArray();
        //判断是否是 [ 开始
        match('[');
        //下标位置小于json字符串的长度时进入循环
        while (index < json.length()) {
            //跳过空格
            skipWhitespace();
            //遇到 ] 结束循环
            if (json.charAt(index) == ']') {
                index++;
                break;
            }
            //解析值
            array.add(parseValue());
            //跳过空格
            skipWhitespace();
            //处理后面的分隔符(,)
            if (index < json.length() && json.charAt(index) == ',') {
                index++;
            }
        }
        //返回解析后的数组对象
        return array;
    }


    /**
     * 解析值
     *
     * @return 解析后的值
     */
    private Object parseValue() {
        //跳过刚开始的空格
        skipWhitespace();
        //获取当前字符
        char c = json.charAt(index);
        //判断当前字符是什么类型
        switch (c) {
            case '"': //字符串
                return parseString();
            case '{': //对象
                return parseObject();
            case '[': //数组
                return parseArray();
            case 't': //布尔值
            case 'f':
                return parseBoolean();
            default: //数字
                return parseNumber();
        }
    }

    /**
     * 解析数字
     *
     * @return 解析后的数字
     */
    private Number parseNumber() {
        //记录开始的位置
        int start = index;
        //判断是不是小数
        boolean isDouble = false;
        //下标位置小于json字符串的长度时进入循环
        while (index < json.length()) {
            //获取当前字符
            char c = json.charAt(index);
            //判断字符是不是数字
            if (Character.isDigit(c) || c == '-' || c == '+') {
                //后移下标
                index++;
            }
            //检查是不是浮点数
            else if (c == '.' || c == 'e' || c == 'E') {
                //标记为浮点数
                isDouble = true;
                //后移动下标
                index++;
            } else {
                //遇到不是数字的时候结束循环
                break;
            }
        }
        //截取字符串
        String numberStr = json.substring(start, index);
        //如果是浮点数就返回浮点数，否则返回整数
        try {
            if (isDouble) {
                return Double.parseDouble(numberStr);
            } else {
                return Long.parseLong(numberStr);
            }
        } catch (NumberFormatException e) {
            throw new JsonException("无效的数字", e);
        }
    }

    /**
     * 解析布尔值
     *
     * @return 解析后的布尔值
     */
    private boolean parseBoolean() {
        //判断是不是true
        if (json.startsWith("true", index)) {
            //下标位置移动4
            index += 4;
            return true;
        }
        //判断是不是false
        if (json.startsWith("false", index)) {
            //下标位置移动5
            index += 5;
            return false;
        }
        throw new JsonException("无效的布尔值");
    }

    /**
     * 解析字符串
     *
     * @return 解析后的字符串
     */
    private String parseString() {
        //判断是否是 " 开始
        match('"');
        //用StringBuilder来拼接字符串
        StringBuilder sb = new StringBuilder();
        //下标位置小于json字符串的长度时进入循环
        while (index < json.length()) {
            //获取单个字符 index++移动字符的下标
            char c = json.charAt(index++);
            //遇到 " 就结束循环
            if (c == '"') {
                break;
            }
            //java字符串中以\\开始的转义字符需要特殊处理
            if (c == '\\') {
                //获取下一个字符
                c = json.charAt(index++);
                sb.append(handleEscapeChar(c));
            } else {
                //不是转义字符就直接添加
                sb.append(c);
            }
        }
        return sb.toString();
    }


}
